{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "52b4084f",
   "metadata": {},
   "source": [
    "在 LangChain 生态中，Text Splitters（文本拆分器） 是处理长文本的核心组件，其核心作用是解决大语言模型（LLM）的上下文窗口限制，同时优化检索精度（如 RAG 场景）和降低计算成本。\n",
    "\n",
    "理解 Text Splitters 需掌握 3 个核心参数，所有拆分逻辑均围绕它们展开：\n",
    "\n",
    "概念|\t定义|\t作用|\n",
    "|:--|:--|:--\n",
    "chunk_size|\t每个拆分后片段（chunk）的最大长度（单位：字符数或 Token 数，依拆分器类型而定）|\t控制片段大小，确保适配模型窗口\n",
    "chunk_overlap|\t相邻两个 chunk 之间的重叠长度（单位与 chunk_size 一致）\t|避免拆分导致的 “语义断裂”（如一句话被拆成两个 chunk，重叠部分衔接信息）\n",
    "separator|\t拆分的 “分隔符”（如换行符 \\n\\n、句号 .、空格 ）\t|按语义边界拆分（优先用段落、句子分隔，而非随机截断，保持语义完整）"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5ddc05b4",
   "metadata": {},
   "source": [
    "# LangChain 核心 Text Splitter 类型\n",
    "LangChain v0.3 中，Text Splitters 主要分为 通用型 和 场景化型，官方推荐优先使用 通用型中的 RecursiveCharacterTextSplitter（适配 90% 以上的文本场景）。以下是常用类型的对比和适用场景：\n",
    "\n",
    "## 2.1 通用型拆分器（最常用）\n",
    "1. RecursiveCharacterTextSplitter（推荐默认）\n",
    "   - 核心逻辑：递归分层拆分—— 优先按 “大分隔符” 拆分（如段落 \\n\\n），若拆分后仍超过 chunk_size，再按 “小分隔符” 拆分（如句子 \\n、句号 . 、空格 ），直到片段符合大小要求。\n",
    "   - 优点：最大限度保留语义完整性（优先按自然语义边界拆分，而非暴力截断），适配所有文本类型（文档、报告、对话记录等）。\n",
    "   - 适用场景：无特殊格式的通用文本（如 TXT 文档、网页内容、新闻稿）。\n",
    "2. CharacterTextSplitter（基础款）\n",
    "   - 核心逻辑：按固定分隔符拆分—— 仅使用单一分隔符（如 \\n\\n 或 .），若拆分后片段仍超过 chunk_size，则直接按字符数截断（可能破坏语义，如截断一句话）。\n",
    "   - 优点：逻辑简单，速度快；\n",
    "   - 缺点：容易破坏语义（如将 “北京是中国的首都” 拆分为 “北京是中” 和 “国的首都”）。\n",
    "   - 适用场景：文本结构极简单（如纯短句列表），或仅需快速拆分无需语义完整性的场景。\n",
    "3. TokenTextSplitter（精准适配模型）\n",
    "   - 核心逻辑：按 Token 数 拆分（而非字符数），直接匹配 LLM/Embedding 模型的 Token 限制（如 OpenAI Embedding 的 Token 限制为 8191）。\n",
    "   - 优点：精准控制输入长度，避免因 “字符数≠Token 数” 导致的超窗口问题（例如中文 1 字符≈1 Token，英文 1 单词≈1.3 Token）。\n",
    "   - 适用场景：对 Token 长度敏感的场景（如 Embedding 生成、LLM 批量调用）。\n",
    "\n",
    "## 2.2 场景化拆分器（针对特定格式）\n",
    "LangChain 提供了针对特定文本格式的拆分器，按 “格式专属分隔符” 拆分，进一步保留结构语义：\n",
    "\n",
    "拆分器类型|\t核心逻辑|\t适用场景|\n",
    "|:--|:--|:--|\n",
    "MarkdownTextSplitter|\t按 Markdown 结构拆分（如 # 标题、## 子标题、--- 分割线、段落 \\n\\n）\t|Markdown 文档（如笔记、博客）\n",
    "PythonCodeTextSplitter|\t按 Python 代码结构拆分（如 def 函数、class 类、# 注释块）\t|Python 代码文件\n",
    "CSVTextSplitter|\t按 CSV 行 / 列拆分（支持按表头分组，避免单行列数据丢失上下文）\t|CSV 数据文件\n",
    "PDFPlumberTextSplitter|\t结合 PDFPlumber 加载器，按 PDF 页面、段落拆分（保留 PDF 格式信息）\t|PDF 文档（需配合 PDF 加载器）"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aecb61e1",
   "metadata": {},
   "source": [
    "# 示例\n",
    "## 通用拆分器\n",
    "LangChain v0.3 中，Text Splitters 已独立为 langchain-text-splitters 包，需单独安装。以下是完整实战流程（以 “加载 TXT 文档并拆分” 为例）："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4b01f207",
   "metadata": {},
   "outputs": [],
   "source": [
    "%pip install langchain-text-splitters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "782a846e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "原始文档长度（字符数）： 283\n",
      "\n",
      "拆分后总片段数：3\n",
      "\n",
      "--- 片段 1 ---\n",
      "长度（字符数）：61\n",
      "内容：人工智能（AI）是当前科技领域的核心趋势之一。它通过模拟人类智能，实现了图像识别、自然语言处理、自动驾驶等多个领域的突破。\n",
      "元数据：{'source': '../../example.txt'}\n",
      "\n",
      "--- 片段 2 ---\n",
      "长度（字符数）：91\n",
      "内容：在自然语言处理（NLP）领域，大语言模型（LLM）是关键技术。例如 DeepSeek、GPT-4 等模型，能理解和生成人类级别的文本，广泛应用于聊天机器人、内容创作、代码生成等场景。\n",
      "元数据：{'source': '../../example.txt'}\n",
      "\n",
      "--- 片段 3 ---\n",
      "长度（字符数）：127\n",
      "内容：然而，LLM 存在上下文窗口限制——无法处理超过窗口长度的文本。因此，Text Splitters 成为 RAG 等场景的核心组件：它将长文本拆分为多个小片段（chunk），确保每个片段能被 LLM 或 Embedding 模型处理，同时保留语义完整性。\n",
      "元数据：{'source': '../../example.txt'}\n"
     ]
    }
   ],
   "source": [
    "# 以 RecursiveCharacterTextSplitter（官方推荐）为例，完整代码如下：\n",
    "\n",
    "# 1. 导入依赖\n",
    "from langchain_community.document_loaders import TextLoader  # 加载 TXT 文档\n",
    "from langchain_text_splitters import RecursiveCharacterTextSplitter  # 递归字符拆分器\n",
    "\n",
    "# 2. 加载文档（生成 Documents 对象，含 page_content 和 metadata）\n",
    "loader = TextLoader(\"../../example.txt\", encoding=\"utf-8\")  # 指定文档路径\n",
    "documents = loader.load()  # 加载后得到 [Document] 列表（单个文档则长度为 1）\n",
    "print(\"原始文档长度（字符数）：\", len(documents[0].page_content))  # 输出原始文本长度\n",
    "\n",
    "# 3. 初始化 Text Splitter（配置核心参数）\n",
    "text_splitter = RecursiveCharacterTextSplitter(\n",
    "    chunk_size = 150,  # 每个 chunk 最大 150 字符\n",
    "    chunk_overlap = 20,  # 相邻 chunk 重叠 20 字符（避免语义断裂）\n",
    ")\n",
    "\n",
    "# 4. 执行拆分（输入 Documents 列表，输出拆分后的 [Document] 列表）\n",
    "split_docs = text_splitter.split_documents(documents)\n",
    "\n",
    "# 5. 查看拆分结果\n",
    "print(f\"\\n拆分后总片段数：{len(split_docs)}\")\n",
    "for i, doc in enumerate(split_docs, 1):\n",
    "    print(f\"\\n--- 片段 {i} ---\")\n",
    "    print(f\"长度（字符数）：{len(doc.page_content)}\")\n",
    "    print(f\"内容：{doc.page_content}\")\n",
    "    print(f\"元数据：{doc.metadata}\")  # 保留原始文档的元数据（如文件路径）"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d07f40bb",
   "metadata": {},
   "source": [
    "## 不同拆分器的实战示例"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "11b0f376",
   "metadata": {},
   "source": [
    "**TokenTextSplitter（按 Token 拆分）**\n",
    "\n",
    "若需适配 Embedding 模型的 Token 限制（如 OpenAI Embedding 限制 8191 Token），可使用 TokenTextSplitter："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8f398ba6",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_text_splitters import TokenTextSplitter\n",
    "\n",
    "# 初始化 Token 拆分器（按 Token 数配置）\n",
    "token_splitter = TokenTextSplitter(\n",
    "    chunk_size=100,  # 每个 chunk 最大 100 Token\n",
    "    chunk_overlap=10  # 重叠 10 Token\n",
    ")\n",
    "\n",
    "# 拆分文本（支持直接拆分字符串，或 split_documents 拆分 Documents）\n",
    "split_texts = token_splitter.split_text(documents[0].page_content)\n",
    "print(f\"按 Token 拆分后片段数：{len(split_texts)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cae1f554",
   "metadata": {},
   "source": [
    "**MarkdownTextSplitter（按 Markdown 结构拆分）**\n",
    "\n",
    "若处理 Markdown 文档（如 example.md），可保留标题层级："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "091b361c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Markdown 片段：# 安装嵌入模型\n",
      "在 windows 下打开 powershell 终端，执行以下命令：\n",
      "\n",
      "```b...\n",
      "Markdown 片段：$env:HF_ENDPOINT = \"https://hf-mirror.com\"  # 设置国内...\n"
     ]
    }
   ],
   "source": [
    "from langchain_community.document_loaders import TextLoader\n",
    "from langchain_text_splitters import MarkdownTextSplitter\n",
    "\n",
    "# # 加载 Markdown 文档\n",
    "# md_loader = UnstructuredMarkdownLoader(\"../环境搭建.md\")\n",
    "# md_docs = md_loader.load()\n",
    "\n",
    "md_loader = TextLoader(\"../环境搭建.md\", encoding=\"utf-8\")\n",
    "md_docs = md_loader.load()\n",
    "\n",
    "# 初始化 Markdown 拆分器（按标题拆分）\n",
    "md_splitter = MarkdownTextSplitter(\n",
    "    chunk_size=200,\n",
    "    chunk_overlap=10\n",
    ")\n",
    "\n",
    "# 拆分\n",
    "md_split_docs = md_splitter.split_documents(md_docs)\n",
    "for doc in md_split_docs:\n",
    "    print(f\"Markdown 片段：{doc.page_content[:50]}...\")  # 打印前 50 字符"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ea9990fc",
   "metadata": {},
   "source": [
    "官方参考文档：[text-splitters](https://python.langchain.com/docs/how_to/#text-splitters)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
